package com.nosnow.gmall.manage.service.impl;

import com.alibaba.dubbo.config.annotation.Service;
import com.alibaba.fastjson.JSON;
import com.nosnow.gmall.bean.*;
import com.nosnow.gmall.config.RedisUtil;
import com.nosnow.gmall.manage.constant.ManageConst;
import com.nosnow.gmall.manage.mapper.*;
import com.nosnow.gmall.service.ManageService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.exceptions.JedisConnectionException;

import java.util.List;

/**
 * @author bingheng
 */
@Service
public class ManageServiceImpl implements ManageService {

    @Autowired
    private BaseCatalog1Mapper baseCatalog1Mapper;

    @Autowired
    private BaseCatalog2Mapper baseCatalog2Mapper;

    @Autowired
    private BaseCatalog3Mapper baseCatalog3Mapper;

    @Autowired
    private BaseAttrInfoMapper baseAttrInfoMapper;

    @Autowired
    private BaseAttrValueMapper baseAttrValueMapper;

    @Autowired
    private SpuInfoMapper spuInfoMapper;

    @Autowired
    private BaseSaleAttrMapper baseSaleAttrMapper;

    @Autowired
    private SpuImageMapper spuImageMapper;

    @Autowired
    private SpuSaleAttrMapper spuSaleAttrMapper;

    @Autowired
    private SpuSaleAttrValueMapper spuSaleAttrValueMapper;

    @Autowired
    private SkuInfoMapper skuInfoMapper;

    @Autowired
    private SkuImageMapper skuImageMapper;

    @Autowired
    private SkuAttrValueMapper skuAttrValueMapper;

    @Autowired
    private SkuSaleAttrValueMapper skuSaleAttrValueMapper;

    @Autowired
    private RedisUtil redisUtil;


    @Override
    public List<BaseCatalog1> getCatalog1() {
        return baseCatalog1Mapper.selectAll();
    }

    @Override
    public List<BaseCatalog2> getCatalog2(Long catalog1Id) {
        BaseCatalog2 baseCatalog2 = new BaseCatalog2();
        baseCatalog2.setCatalog1Id(catalog1Id);
        return baseCatalog2Mapper.select(baseCatalog2);
    }

    @Override
    public List<BaseCatalog3> getCatalog3(Long catalog2Id) {
        BaseCatalog3 baseCatalog3 = new BaseCatalog3();
        baseCatalog3.setCatalog2Id(catalog2Id);
        return baseCatalog3Mapper.select(baseCatalog3);
    }

    @Override
    public List<BaseAttrInfo> getAttrList(Long catalog3Id) {
        return baseAttrInfoMapper.getBaseAttrInfoListByCatalog3Id(catalog3Id);
    }

    @Override
    public List<BaseAttrInfo> getAttrList(List valueIdList) {

        String valueIds = StringUtils.join(valueIdList, ",");
        System.out.println(valueIds);

        List<BaseAttrInfo> baseAttrInfoListByValueIds = baseAttrInfoMapper.getBaseAttrInfoListByValueIds(valueIds);

        return baseAttrInfoListByValueIds;
    }

    @Override
    public void saveAttrInfo(BaseAttrInfo baseAttrInfo) {
        // 如果有主键就进行更新，没有就insert
        if(baseAttrInfo.getId() != null) {
            baseAttrInfoMapper.updateByPrimaryKey(baseAttrInfo);
        } else {
            baseAttrInfoMapper.insertSelective(baseAttrInfo);
        }

        // 把原属性值全部清空
        BaseAttrValue baseAttrValue = new BaseAttrValue();
        baseAttrValue.setAttrId(baseAttrInfo.getId());
        baseAttrValueMapper.delete(baseAttrValue);

        // 重新插入属性值
        if(baseAttrInfo.getAttrValueList() != null && baseAttrInfo.getAttrValueList().size() > 0) {
            for(BaseAttrValue attrValue : baseAttrInfo.getAttrValueList()) {
                // baseAttrInfo.getId()的值为刚刚保存的，如何立刻得到呢？
                attrValue.setAttrId(baseAttrInfo.getId());
                baseAttrValueMapper.insertSelective(attrValue);
            }
        }
    }

    @Override
    public List<SpuInfo> getSpuInfoList(SpuInfo spuInfo) {
        return spuInfoMapper.select(spuInfo);
    }

    @Override
    public List<BaseSaleAttr> getBaseSaleAttrList() {
        return baseSaleAttrMapper.selectAll();
    }

    @Override
    public void saveSpuInfo(SpuInfo spuInfo) {

        if(spuInfo.getId() != null && spuInfo.getId().toString().length() == 0) {
            spuInfo.setId(null);
        }
        spuInfoMapper.insertSelective(spuInfo);
        // 暂时这么写，后期改为批量更新
        List<SpuImage> spuImageList = spuInfo.getSpuImageList();
        if(spuImageList != null && spuImageList.size() > 0) {
            for (SpuImage spuImage : spuImageList) {
                spuImage.setSpuId(spuInfo.getId());
                spuImageMapper.insertSelective(spuImage);
            }
        }
        List<SpuSaleAttr> spuSaleAttrList = spuInfo.getSpuSaleAttrList();
        if(spuSaleAttrList != null && spuSaleAttrList.size() > 0) {
            for (SpuSaleAttr spuSaleAttr : spuSaleAttrList) {
                List<SpuSaleAttrValue> spuSaleAttrValueList = spuSaleAttr.getSpuSaleAttrValueList();
                if(spuSaleAttrValueList != null && spuSaleAttrValueList.size() > 0) {
                    for (SpuSaleAttrValue spuSaleAttrValue : spuSaleAttrValueList) {
                        spuSaleAttrValue.setSpuId(spuInfo.getId());
                        spuSaleAttrValueMapper.insertSelective(spuSaleAttrValue);
                    }
                }
                spuSaleAttr.setSpuId(spuInfo.getId());
                spuSaleAttrMapper.insertSelective(spuSaleAttr);
            }
        }
    }

    @Override
    public List<SpuImage> getSpuImageList(String spuId) {
        SpuImage spuImageQuery = new SpuImage();
        spuImageQuery.setSpuId(Long.valueOf(spuId));
        return spuImageMapper.select(spuImageQuery);
    }

    @Override
    public List<SpuSaleAttr> getSpuSaleAttrList(String spuId) {

        return spuSaleAttrMapper.selectSpuSaleAttrList(Long.parseLong(spuId));
    }

    @Override
    public void saveSkuInfo(SkuInfo skuInfo) {
        if(skuInfo.getId() != null && skuInfo.getId().toString().length() == 0) {
            skuInfo.setId(null);
        }
        skuInfoMapper.insertSelective(skuInfo);
        List<SkuImage> skuImageList = skuInfo.getSkuImageList();
        if(skuImageList != null && skuImageList.size() > 0) {
            for (SkuImage skuImage : skuImageList) {
                skuImage.setSkuId(skuInfo.getId());
                skuImageMapper.insertSelective(skuImage);
            }
        }
        List<SkuAttrValue> skuAttrValueList = skuInfo.getSkuAttrValueList();
        if(skuAttrValueList != null && skuAttrValueList.size() > 0) {
            for (SkuAttrValue skuAttrValue : skuAttrValueList) {
                skuAttrValue.setSkuId(skuInfo.getId());
                skuAttrValueMapper.insertSelective(skuAttrValue);
            }
        }
        List<SkuSaleAttrValue> skuSaleAttrValueList = skuInfo.getSkuSaleAttrValueList();
        if(skuSaleAttrValueList != null && skuSaleAttrValueList.size() > 0) {
            for (SkuSaleAttrValue skuSaleAttrValue : skuSaleAttrValueList) {
                skuSaleAttrValue.setSkuId(skuInfo.getId());
                skuSaleAttrValueMapper.insertSelective(skuSaleAttrValue);
            }
        }
    }

    @Override
    public SkuInfo getSkuInfo(String skuId) {

        SkuInfo skuInfo = null;
        try {
            // 获取redis实例化对象
            Jedis jedis = redisUtil.getJedis();
            /**
             * redis key的命名规范：
             * 由于Redis不像数据库表那样有结构，其所有的数据全靠key进行索引，所以redis数据的可读性，全依靠key。
             * 企业中最常用的方式就是：
             *  object:id:field
             *  比如：sku:1314:info、user:1092:password
             */
            String skuInfoKey = ManageConst.SKUKEY_PREFIX + skuId + ManageConst.SKUKEY_SUFFIX;

            // 根据key从redis中获取值
            String skuInfoJson = jedis.get(skuInfoKey);

            // 如果从redis中获取值时没有取到值，则去查询数据库，然后将从数据库中查询到的结果，放入到redis中去
            if(skuInfoJson == null || skuInfoJson.length() == 0) {
                System.err.println(Thread.currentThread().getName() + "-------------未命中缓存-------------------");

                /**
                 * 2. 如果redis缓存在高峰期到期失效，这个时候请求会像雪崩一样，直接访问数据库如何处理？
                 * 答： 这种情况下，先到的线程会拿到一把锁，把门锁住，
                 *      然后拿到锁的这个线程独自去查询数据库，查询到数据库后进行更新缓存，缓存更新完毕其他线程就会从缓存中获取该值
                 *      没有拿到这把锁的线程则会不停的回调该该方法，进行自旋，直到命中缓存
                 *
                 *      下面的代码是实现方式
                 *
                 */
                String skuLockKey = ManageConst.SKUKEY_PREFIX + skuId + ManageConst.SKULOCK_SUFFIX;
                // key value  锁标志  超时标志  超时时间
                String lock  = jedis.set(skuLockKey, "ok", "NX", "PX", ManageConst.SKULOCK_EXPIRE_PX);

                // 如果能够拿到分布式锁
                if("OK".equals(lock)) { // 如果有人拿到锁去数据库进行查询了
                    System.err.println(Thread.currentThread().getName()+"-------------获得分布式锁！-------------");
                    skuInfo = getSkuInfoFromDB(skuId);
                    /**
                     * 3. 如果用户不停地查询一条不存在的数据，缓存没有，数据库也没有，那么会出现什么情况，如何处理？
                     */
                    if(skuInfo == null) {
                        jedis.setex(skuInfoKey, ManageConst.SKUKEY_TIMEOUT, "empty");
                        return null;
                    }
                    String skuInfoJsonNew = JSON.toJSONString(skuInfo);
                    jedis.setex(skuInfoKey, ManageConst.SKUKEY_TIMEOUT, skuInfoJsonNew);
                    System.err.println( Thread.currentThread().getName()+"：--------------------缓存更新完毕--------------------" );
                    jedis.close(); // 关闭缓存
                    return skuInfo;
                } else {// 没有拿到锁的不停的回调自己，进行自旋
                    System.err.println(Thread.currentThread().getName()+"-------------未获得分布式锁， 进行自旋-------------");
                    Thread.sleep(1000L);
                    jedis.close(); // 关闭缓存
                    return getSkuInfo(skuId);
                }
            } else if("empty".equals(skuInfoJson)) { // 3. 如果用户不停地查询一条不存在的数据，缓存没有，数据库也没有，那么会出现什么情况，如何处理？
                return null;
            } else {
                System.err.println( Thread.currentThread().getName()+"-------------命中缓存-------------");
                skuInfo = JSON.parseObject(skuInfoJson, SkuInfo.class);
                jedis.close(); // 关闭缓存
                return skuInfo;
            }
        } catch (JedisConnectionException e) { // redis 链接异常
            // 1、如果redis宕机了，或者链接不上，怎么办？
            e.printStackTrace();

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // redis宕机了，或者直接连接不上了，那么去数据库查询
        skuInfo = getSkuInfoFromDB(skuId);
        return skuInfo;
    }

    @Override
    public SkuInfo getSkuInfoFromDB(String skuId) {
        System.err.println( Thread.currentThread().getName()+"： -------------查询数据库-------------------" );
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        SkuInfo skuInfo = skuInfoMapper.selectByPrimaryKey(Long.valueOf(skuId));
        if(skuInfo != null) {
            SkuImage skuImageQuery = new SkuImage();
            skuImageQuery.setSkuId(skuInfo.getId());
            List<SkuImage> skuImageList = skuImageMapper.select(skuImageQuery);
            skuInfo.setSkuImageList(skuImageList);

            SkuAttrValue skuAttrValueQuery = new SkuAttrValue();
            skuAttrValueQuery.setSkuId(Long.valueOf(skuId));
            List<SkuAttrValue> skuAttrValueList = skuAttrValueMapper.select(skuAttrValueQuery);
            skuInfo.setSkuAttrValueList(skuAttrValueList);

            SkuSaleAttrValue skuSaleAttrValueQuery = new SkuSaleAttrValue();
            skuSaleAttrValueQuery.setSkuId(Long.valueOf(skuId));
            List<SkuSaleAttrValue> skuSaleAttrValueList = skuSaleAttrValueMapper.select(skuSaleAttrValueQuery);
            skuInfo.setSkuSaleAttrValueList(skuSaleAttrValueList);

        }
        return skuInfo;
    }

    @Override
    public List<SpuSaleAttr> getSpuSaleAttrListCheckBySku(SkuInfo skuInfo) {
        return spuSaleAttrMapper.selectSpuSaleAttrListCheckBySku(skuInfo.getId(), skuInfo.getSpuId());
    }

    @Override
    public List<SkuSaleAttrValue> getSkuSaleAttrValueListBySpu(Long spuId) {
        return skuSaleAttrValueMapper.selectSkuSaleAttrValueListBySpu(spuId);
    }


}
